/**
 * 
 */
package upe.dsc.algorithms.swarm.pso.entities;

import upe.dsc.algorithms.Solution;
import upe.dsc.algorithms.swarm.pso.process.PsoBasicImpl;
import upe.dsc.algorithms.swarm.util.Functions;
import upe.dsc.algorithms.util.RandomDouble;

/**
 * @author ajccl
 * 
 */
public class Particle implements Solution {
	public static ParticleType TYPE;

	public enum ParticleType {
		BASIC, INERTIA, CONSTRICTED, EVOLUCIONARY
	};

	protected double[] position;

	protected double[] velocity;

	protected double fitness;

	protected double[] pPosition;

	protected double pFitness;

	public enum FunctionType {
//		SPHERE, SCHWEFEL12, ROSENBROCK, GENERALIZED_SCHWEFEL26, GENERALIZED_RASTRIGIN, ACKLEY
//		GENERALIZED_RASTRIGIN, ACKLEY
		SPHERE, ROSENBROCK, GENERALIZED_GRIEWANK, GENERALIZED_RASTRIGIN
	};

	public static FunctionType FUNCTION_TYPE;

	/**
   * @return the fitness value of the particle
   */
	public double getFitness() {
		return fitness;
	}

	/**
   * @param fitness
   *          The fitness to set up
   */
	public void setFitness(double fitness) {
		this.fitness = fitness;
	}

	/**
   * @return the position vector
   */
	public double[] getPosition() {
		return position;
	}

	public void setPosition(double[] position) {
		System.arraycopy(position, 0, this.position, 0, position.length);
	}

	/**
   * @return the velocity vector
   */
	public double[] getVelocity() {
		return velocity;
	}

	/**
   * @param velocity
   *          The velocity vector to set up
   */
	public void setVelocity(double[] velocity) {
		this.velocity = velocity;
	}

	public double[] getPPosition() {
		return pPosition;
	}

	public void setPPosition(double[] position) {
		System.arraycopy(position, 0, this.pPosition, 0, position.length);
	}

	public double getPFitness() {
		return pFitness;
	}

	public void setPFitness(double fitness) {
		this.pFitness = fitness;
	}

	public Particle(int dimensions) {
		this.initialize(dimensions);
	}

	public Particle(double position[]) {
		this.position = new double[position.length];
		this.pPosition = new double[position.length];

		this.setPosition(position);
		this.calculateFitness();

		this.setPPosition(position);
		this.setPFitness(this.fitness);
	}

	protected void initialize(int dimensions) {
		this.position = new double[dimensions];
		this.velocity = new double[dimensions];
		this.pPosition = new double[dimensions];

		for (int i = 0; i < dimensions; i++) {
			// posicoes de (-POSITION_MAX_VALUE a POSITION_MAX_VALUE)
			double rand = RandomDouble.getRandomDouble(PsoBasicImpl.POSITION_MAX_VALUE, 5);
			this.position[i] = rand;
			this.pPosition[i] = rand;
			// velocidades de (0 a VELOCITY_MAX_VALUE)
			rand = Math.abs(RandomDouble.getRandomDouble(PsoBasicImpl.VELOCITY_MAX_VALUE, 5));
			this.velocity[i] = rand;
		}
		this.setPPosition(this.position);
		this.calculateFitness();
		this.pFitness = this.fitness;
	}

	/**
   * Update particle position according to PSO equations
   */
	protected void updatePosition(double c1, double c2) {
	}

	public void updateParticleInformation() {
		this.setPPosition(this.position);
		this.setPFitness(this.fitness);
	}

	public void calculateFitness() {
		// Sphere
		if (FUNCTION_TYPE == FunctionType.SPHERE) {
			this.fitness = 1.0 / Functions.sphere(this.position);
		}
//
//		// Schwefel 1.2
//		if (FUNCTION_TYPE == FunctionType.SCHWEFEL12) {
//			this.fitness = 1.0 / Functions.schwefel12(this.position);
//		}
//
		// Rosenbrock
		if (FUNCTION_TYPE == FunctionType.ROSENBROCK) {
			this.fitness = 1.0 / Functions.rosenbrock(this.position);
		}
//
//		// Generalized Schwefel 2.6
//		if (FUNCTION_TYPE == FunctionType.GENERALIZED_SCHWEFEL26) {
//			this.fitness = 1.0 / Functions.generalizedSchwefel26(this.position);
//		}

		// Generalized Rastrigin
		if (FUNCTION_TYPE == FunctionType.GENERALIZED_RASTRIGIN) {
			this.fitness = 1.0 / Functions.generalizedRastrigin(this.position);
		}

/*		// Ackley
		if (FUNCTION_TYPE == FunctionType.ACKLEY) {
			this.fitness = 1.0 / Functions.ackley(this.position);
		}		
*/		
		// Generalized Rastrigin
		if (FUNCTION_TYPE == FunctionType.GENERALIZED_GRIEWANK) {
			double result = Functions.generalizedGriewank(this.position);
			this.fitness = 1.0 / result;
		}
		// double sixhumpCamelback = Functions.sixhumpCamelback(this.position);

		// this.fitness = 1.0 / rosenbrock;
	}
}
